package com.project.ssmfo.controller.common;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.project.ssmfo.common.Configuration;
import com.project.ssmfo.controller.BaseController;
import com.project.utils.FileUtils;
import com.project.utils.ueditor.AppInfo;
import com.project.utils.ueditor.MIMEType;
import com.project.utils.ueditor.UEActionMap;
import com.project.utils.ueditor.UEditorConfig;

/**
 * <p>ClassName: UEditorController</p>
 * <p>Description: Sprint MVC 集成UEditor Controller
 * 		<br>Controller 分别实现了UE在初始化、上传文件等情况下交互的接口。
 * 		<br>1. 获取UE配置信息接口 getUEConfig
 * 		<br>2. 图像文件上传接口 uploadFileImage
 * 		<br>3. 网络图像抓取接口 catchNetworkImage
 * 		<br>4. 下载图像、文件 downloadFile
 * </p>
 */
@Controller
@RequestMapping("/ueditor")
public class UEditorController  extends BaseController {
	private static Logger logger = LoggerFactory.getLogger(UEditorController.class);
	public static final SimpleDateFormat MILLIS_DATE_FORMATER = new SimpleDateFormat("yyyyMMddHHmmssSSS");
	public static final SimpleDateFormat COMM_DATE_FORMATER = new SimpleDateFormat("yyyyMMdd");
	public static final ObjectMapper objectMapper = new ObjectMapper();
	
	/**
	 * <p>Title: uetest</p>
	 * <p>Description: UEditor 测试页面</p>
	 * @return String
	 */
	@RequestMapping("uetest")
	public String uetest() {
		return "uetest";
	}
	
	/**
	 * <p>Title: getUEConfig</p>
	 * <p>Description: 获取UE所有配置信息，也是UE初始请求的默认方法</p>
	 * @param action
	 * @return String
	 * @throws JsonProcessingException 
	 */
	@RequestMapping("getUEConfig")
	@ResponseBody
	public String getUEConfig(String action) throws JsonProcessingException {
		logger.info("[getUEConfig] get ueditor all config info ...");
		Map<String, Object> resultMap = new HashMap<String, Object>();
		if ( action == null || !UEActionMap.mapping.containsKey( action ) ) {
			logger.error("[getUEConfig] invalid action info ...");
			resultMap.put("state", AppInfo.getStateInfo(AppInfo.INVALID_ACTION));
			return objectMapper.writeValueAsString(resultMap);
		}
		UEditorConfig ueconfig = UEditorConfig.getInstance();
		if (ueconfig == null) {//配置信息为空，获取ue配置信息失败
			logger.error("[getUEConfig] unable get ueconfig info ...");
			resultMap.put("state", AppInfo.getStateInfo(AppInfo.CONFIG_ERROR));
			return objectMapper.writeValueAsString(resultMap);
		}
		
		return objectMapper.writeValueAsString(ueconfig);
	}
	
	/**
	 * <p>Title: uploadFileImage</p>
	 * <p>Description: 上传图像、文件
	 * 	<br>注意：此处使用了produces，正常情况来说是不用的，但是这里比较特殊
	 * 	UEditor 这里上传文件可能有两种方式，一是采用Ajax上传这种方式只要加@ResponseBody就可以没有问题。二就是ue采用的一种比较特别的方式，使用普通页面form上传，但是
	 * 要求返回json形式数据，而spring MVC在处理页面请求的时候，会根据请求的ContentType来设置返回的ContentType。这种机制如果对于sitemesh来说就是比较致命的了，
	 * 由于普通页面的原因，sitemesh会根据页面ContentType来判定页面（返回数据）需要装饰，这就造成了，我们返回的是json，结果中间又被sitemesh装饰了一次，导致前端
	 * 页面无法正常解析。@RequestMapping 的produces很好的解决了这个问题，通过这个属性来设置返回数据的类型为 application/json，这样sitemesh就不会再装饰
	 * 数据了。
	 * </p>
	 * @param uploadType
	 * @param uploadDate
	 * @param uploadFileName
	 * @return Map<String,Object>
	 * @throws IOException 
	 */
	@RequestMapping(value="/uploadFileImage",produces="application/json")
	@ResponseBody
	public Map<String, Object> uploadFileImage(@RequestParam("upfile") MultipartFile upfile) throws IOException{
		// 返回结果信息(UEditor需要)
		Map<String, Object> resultMap = new HashMap<String, Object>();
		if (!upfile.isEmpty()) {
			
			String originName = upfile.getOriginalFilename();
			Long fileSize = upfile.getSize();
			String fileType = originName.substring(originName.lastIndexOf("."));
			String storeName = generateFileName() + fileType;
			String currDateFormat = COMM_DATE_FORMATER.format(new Date());
			String filePathWithName = "/" + currDateFormat + "/" + storeName;
//			FileUtils.uploadfile(upfile.getInputStream(), filePathWithName);
			String storePath = Configuration.getUeditorFileSavedir() +"/"+ currDateFormat;
			File dir = new File(storePath);
			if(!dir.exists()){
				dir.mkdirs();
			}
			// 按照路径新建文件
			File newFile = new File(storePath + "/" + storeName);
			try {
				upfile.transferTo(newFile);
			} catch (IOException e) {
				e.printStackTrace();
				resultMap.put("state", "FAILED");
				return resultMap;
			}
			
			resultMap.put("state", "SUCCESS");
			resultMap.put("title", storeName);
			resultMap.put("original", originName);
			resultMap.put("type", fileType);
			resultMap.put("url", filePathWithName);
			resultMap.put("size", fileSize.toString());
		}else {
			resultMap.put("state", "FAILED");
		}
		return resultMap;
	}
	
	/**
	 * <p>Title: catchNetworkImage</p>
	 * <p>Description: 抓取网络图像文件（也可以抓取普通文件）</p> void
	 * @param source String类型 URL数组
	 */
	@RequestMapping(value="/catchNetworkImage",produces="application/json")
	@ResponseBody
	public Map<String, Object> catchNetworkImage(String[] source) {
//		String[] source = getHttpRequest().getParameterValues("source[]");
		Map<String, Object> resultMap = new HashMap<String, Object>();
		if(source == null || source.length == 0) {
			resultMap.put("state", "FAILED");
			return resultMap;
		}
		List<Map<String, Object>> fileStateList = new ArrayList<Map<String,Object>>();
		for(String imgurl : source) {
			fileStateList.add(saveImageFromUrl(imgurl));
		}
		resultMap.put("state", "SUCCESS");
		resultMap.put("list", fileStateList);
		return resultMap;
	}
	
	private Map<String, Object> saveImageFromUrl(String urlStr) {
		HttpURLConnection connection = null;
		URL url = null;
		String suffix = null;
		Map<String, Object> resultMap = new HashMap<String, Object>();
		try {
			url = new URL( urlStr );

			if ( !validHost( url.getHost() ) ) {
				resultMap.put("state", AppInfo.getStateInfo(AppInfo.PREVENT_HOST));
				return resultMap;
			}
			connection = (HttpURLConnection) url.openConnection();
			connection.setInstanceFollowRedirects( true );
			connection.setUseCaches( true );
			if ( HttpURLConnection.HTTP_OK != connection.getResponseCode()) {
				resultMap.put("state", AppInfo.getStateInfo(AppInfo.CONNECTION_ERROR));
				return resultMap;
			}
			
			suffix = MIMEType.getSuffix( connection.getContentType() );
			
			Map<String, Object> config = UEditorConfig.getInstance().getConfig(UEActionMap.CATCH_IMAGE);
			
			String[] allowFiles = (String[])config.get("allowFiles");
			boolean isAllowFileType = false;
			for(String ft : allowFiles) {
				if(ft.equals(suffix)) {
					isAllowFileType = true;
					break;
				}
			}
			//验证文件类型
			if (!isAllowFileType) {
				resultMap.put("state", AppInfo.getStateInfo(AppInfo.NOT_ALLOW_FILE_TYPE));
				return resultMap;
			}
			//验证文件大小
			Integer maxSize = (Integer)config.get("maxSize");
			if ( connection.getContentLength() > maxSize ) {
				resultMap.put("state", AppInfo.getStateInfo(AppInfo.MAX_SIZE));
				return resultMap;
			}
			
			String storeName;
			String currDateFormat;
			File destFile;
			try {
				storeName = generateFileName() + suffix;
				currDateFormat = COMM_DATE_FORMATER.format(new Date());
				String filePathWithName = "/" + currDateFormat + "/" + storeName;
//				FileUtils.uploadfile(connection.getInputStream(), filePathWithName);
				String baseStoreDir = Configuration.getUeditorFileSavedir();
				String physicalPath = baseStoreDir + "/" + currDateFormat + "/" + storeName;
				destFile = new File(physicalPath);
				org.apache.commons.io.FileUtils.copyInputStreamToFile(connection.getInputStream(), destFile);
				resultMap.put("state", "SUCCESS");
				resultMap.put("size", destFile.length());
				resultMap.put("title", destFile.getName());
				resultMap.put("url", filePathWithName );
				resultMap.put("source", urlStr );
			} catch (Exception e) {
				resultMap.put("state", AppInfo.getStateInfo(AppInfo.IO_ERROR));
				return resultMap;
			}
			return resultMap;
			
		} catch ( Exception e ) {
			resultMap.put("state", AppInfo.getStateInfo(AppInfo.REMOTE_FAIL));
			return resultMap;
		}
	}
	
	private boolean validHost( String hostname ) {
		try {
			InetAddress ip = InetAddress.getByName(hostname);
			if (ip.isSiteLocalAddress()) {
				return false;
			}
		} catch (UnknownHostException e) {
			return false;
		}
		return true;
	}
	
//	private void sendResponse(Object result) {
//		if(result == null) return;
//		try(PrintWriter writer = getHttpResponse().getWriter();) {
//			if(result instanceof String) {
//				writer.write((String)result);
//			}else {
//				writer.write(objectMapper.writeValueAsString(result));
//			}
//			writer.flush();
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//	}
	
	/**
	 * <p>Title: downloadFile</p>
	 * <p>Description: 下载图片附件</p>
	 * @param fileType
	 * @param fileDate
	 * @param fileName
	 * @param upfile void
	 */
	@RequestMapping("/downloadFile/{fileDate}/{fileName}.{fileType}")
	@ResponseBody
	public void downloadFile(
			 @PathVariable String fileDate,  
			 @PathVariable String fileName,
			 @PathVariable String fileType
			){
		String baseStoreDir = Configuration.getUeditorFileSavedir();
		String storePath = baseStoreDir + "/" + fileDate;
		FileUtils.downFile(getHttpResponse(), storePath, fileName + "." + fileType, fileName + "." + fileType);
	}
	
    public static String generateFileName() {
		int hashCodeV = UUID.randomUUID().toString().hashCode();
		if (hashCodeV < 0) {// 有可能是负数
			hashCodeV = -hashCodeV;
		}
		// 0 代表前面补充0
		// 4 代表长度为4
		// d 代表参数为正数型
		return MILLIS_DATE_FORMATER.format(new Date()) + String.format("%08d", hashCodeV);
	}
}
